#include "rebuilding_operations.h"
#include "mainwindow.h"
#include "resource.h"

EmbossSettings embossSettings;
GaussianSettings gaussianSettings;
AverageSettings averageSettings;


# pragma region flipHorizontal
bool flipHorizontalSetup( Task *task )
{
	return true;
}

/*
	Naam: Gerco Koks
*/
bool flipHorizontal( Image &imgSource, Image &imgResult, void *data )
{
	// declaratie van de members
	unsigned int row, col, chan;
	unsigned int lastBit;
	
	// loop door de rijen
	for( row = 0; row < imgSource.height(); row++ )
	{
		// -1 omdat er gerekend wordt vanaf 0
		lastBit = (imgSource.width()-1); 
		// loop door de kolommen
		for( col = 0; col < imgSource.width(); col++, lastBit-- )
			// loop door de channels
			for( chan = 0; chan < 3; chan++ )
				// spiegel de bitjes in de source afbeelding
				imgResult[row][lastBit][chan] = imgSource[row][col][chan];
	}
	// rotzooi opruimen
	delete data;
	// en klaar is kees
	return true;
}
#pragma endregion

#pragma region flipVertical
bool flipVerticalSetup( Task *task )
{
	return true;
}

/*
	Naam: Gerco Koks
*/
bool flipVertical( Image &imgSource, Image &imgResult, void *data )
{
	// declaratie van de members
	unsigned int row, col, chan;
	unsigned int lastBit;
	// -1 omdat er gerekend wordt vanaf 0
	lastBit = (imgSource.height()-1); 
	// loop door de rijen
	for( row = 0; row < imgSource.height(); row++, lastBit-- )
	{
		// loop door de kolommen
		for( col = 0; col < imgSource.width(); col++ )
			// loop door de channels
			for( chan = 0; chan < 3; chan++ )
				// spiegel de bitjes in de source afbeelding
				imgResult[lastBit][col][chan] = imgSource[row][col][chan];
	}
	// rotzooi opruimen
	delete data;
	// en klaar is kees
	return true;
}
#pragma endregion

#pragma region median
bool medianSetup( Task *task )
{
	return true;
}

/*
	gemaakt door Gerco Koks
	het masker gaat per pixel over de afbeelding heen.
	DE waardes in het masker worden van klein naar groot gesorteerd.
	de middelste waarde wordt de actieve pixel waarde.
*/
bool median( Image &imgSource, Image &imgResult, Task *task )
{
	unsigned int row, col, chan;
	const int paddedSize = 1;
	std::vector<double> mask;
	mask.resize( 9 );
	int maskSize = mask.size();
	int dimension = 1;

		
	// padding klasse instantieren
	Padding pad;
	// zet zeropadding om imgSource
	Image imgPadded = pad.zeroPadding( imgSource, paddedSize, task );
		
	// loop door de rijen
	for( row = paddedSize; row < (imgPadded.height() - paddedSize); row++ )
		// loop door de kolommen
		for( col = paddedSize; col < (imgPadded.width() - paddedSize); col++ )
			// loop door de channels
			for( chan = 0; chan < 3; chan++ )
			{
				int stradeR = dimension;
				int stradeC = dimension;

				for( int i = 0; i < maskSize; i++ )
				{
					mask[i] = imgPadded[row-stradeR][col-stradeC][chan];	

					if( stradeR != (-dimension) )
						stradeR--;
					else
					{
						stradeR = dimension;
						stradeC--;
					}
					
				}
				// sorteren, dit is een erg dure operatie
				sort(mask.begin(), mask.end());
				// zet de middelste pixel in resultimage
				imgResult[row - paddedSize][col - paddedSize][chan] = (byte)mask[4];
			}
	// en klaar is kees
	return true;
		
}
#pragma endregion

#pragma region average
BOOL CALLBACK averageSetupProc(
	HWND hWndDialog,
	UINT message,
	WPARAM wParam,
	LPARAM lParam )
{
	switch( message )
	{
	case WM_INITDIALOG:
	case WM_COMMAND:
		switch( LOWORD( wParam ) )
		{
		case IDOK:
			{
				if(IsDlgButtonChecked( hWndDialog, IDC_RADIO_3x3))
				{
					int mode = 0;
					averageSettings._mode = mode;
				}
				if(IsDlgButtonChecked( hWndDialog, IDC_RADIO_5x5))
				{
					int mode = 1;
					averageSettings._mode = mode;
				}
				if (IsDlgButtonChecked( hWndDialog, IDC_RADIO_7x7))
				{
					int mode = 2;
					averageSettings._mode = mode;				
				}
			}
		case IDCANCEL:
			EndDialog( hWndDialog, wParam );
			return TRUE;
		}
		break;
	}
	return FALSE;
}

bool averageSetup( Task *task )
{
	MainWindow *mainWindow = MainWindow::instance();
	if( DialogBox(
		mainWindow->hInstance(),
		MAKEINTRESOURCE( IDD_DIALOG_AVERAGE ),
		mainWindow->hWnd(),
		(DLGPROC) &averageSetupProc) == IDOK )
	{
		task->_data = new AverageSettings( averageSettings );
		return true;
	}

	return false; //The user cancelled the operation.
}
 
/*
	Gemaakt door Bas Janssens
	Aangepast door Gerco Koks
	Het masker wordt nu dynamisch gevuld in een loop
*/
bool average( Image &imgSource, Image &imgResult, Task *task )
{
	// members voor de loop
	unsigned int row, col, chan;
	// een vector wordt als masker gebruikt
	std::vector<double> mask;
	
	int dimension;
	// welke modus kies de gebruiker
	int mode = averageSettings._mode;
	
	// afhankelijk van de mode wordt de dimension en de grote van het masker bepaald
	if( mode == 0 )
	{
		mask.resize( 9 ); 
		dimension = 1;
	}
	else if( mode == 1 )
	{
		mask.resize( 25 );
		dimension = 2;
	}
	else if( mode == 2 )
	{
		mask.resize( 49 );
		dimension = 3;
	}

	// padding size
	const int paddedSize = dimension;
	// padding klasse instantieren
	Padding pad;
	// zet zeropadding om imgSource
	Image imgPadded = pad.zeroPadding( imgSource, paddedSize, task );

	int maskSize = mask.size();
	
	// loop door de rijen
	for( row = paddedSize; row < (imgPadded.height() - paddedSize); row++ )
		// loop door de kolommen
		for( col = paddedSize; col < (imgPadded.width() - paddedSize); col++ )
			// loop door de channels
			for( chan = 0; chan < 3; chan++ )
			{
				int stradeR = dimension;
				int stradeC = dimension;

				for( int i = 0; i < maskSize; i++ )
				{
					mask[i] = imgPadded[row-stradeR][col-stradeC][chan];	

					if( stradeR != (-dimension) )
						stradeR--;
					else
					{
						stradeR = dimension;
						stradeC--;
					}
					
				}

				double result = 0;
				for( int i = 0; i < maskSize; i++ )
				{
					result += mask[i];
				}
				result = result / maskSize;
				result = ( result > 255 ) ? 255 : ( result < 0 ) ? 0 : result;
				
				// zet de middelste pixel in resultimage
				imgResult[row - paddedSize][col - paddedSize][chan] = (byte)result;
			}
		// en klaar is kees
		return true;
	
	return true;
}


#pragma endregion

#pragma region emboss
BOOL CALLBACK embossSetupProc(
	HWND hWndDialog,
	UINT message,
	WPARAM wParam,
	LPARAM lParam )
{
	switch( message )
	{
	case WM_INITDIALOG:
		//This message is send when the dialog is about to be initialized.
		//We set the default values for the edit controls.
		SetDlgItemText( hWndDialog, IDC_EDIT_EMBOSS_X, L"1" );
		SetDlgItemText( hWndDialog, IDC_EDIT_EMBOSS_Y, L"1" );
		break;
	case WM_COMMAND:
		switch( LOWORD( wParam ) )
		{
		case IDOK:
			{
				//Read the values the user entered and store them in embossSettings.
				wchar_t szXOffset[32], szYOffset[32];

				if( GetDlgItemText( hWndDialog, IDC_EDIT_EMBOSS_X, szXOffset, 32 ) == 0 || 
					GetDlgItemText( hWndDialog, IDC_EDIT_EMBOSS_Y, szYOffset, 32 ) == 0 )
				{
					//One or more fields are left empty. Show a warning and do not close the dialog.
					MessageBox( hWndDialog, L"Invalid input", L"Message", MB_OK | MB_ICONEXCLAMATION );
					return FALSE;
				}

				//If text is entered as the x or y offset, the value will be set to 0 by _wtoi.
				//It is not easy to change this behaviour. Setting the 'Number' propery of the
				//edit box would not allow negative numbers.
				embossSettings._xOffset = _wtoi( szXOffset );
				embossSettings._yOffset = _wtoi( szYOffset );
			}
		case IDCANCEL:
			EndDialog( hWndDialog, wParam );
			return TRUE;
		}
		break;
	}
	return FALSE;
}

bool embossSetup( Task *task )
{
	MainWindow *mainWindow = MainWindow::instance();
	if( DialogBox(
		mainWindow->hInstance(),
		MAKEINTRESOURCE( IDD_DIALOG_EMBOSS ),
		mainWindow->hWnd(),
		(DLGPROC) &embossSetupProc ) == IDOK )
	{
		//Allocate a structure and copy the settings. This structure is used in
		//the emboss method.
		task->_data = new EmbossSettings( embossSettings );
		return true;
	}
	return false; //The user cancelled the operation.
}

bool emboss( Image &imgSource, Image &imgResult, void *data )
{
	int xOffset, yOffset;
	unsigned int xPos, yPos;

	xOffset = ( (EmbossSettings*) data )->_xOffset;
	yOffset = ( (EmbossSettings*) data )->_yOffset;

	for( unsigned int row = 0; row < imgSource.height(); ++row )
	{
		yPos = min( (unsigned int) max( (int) row + yOffset, 0 ), imgSource.height() - 1 );

		for( unsigned int col = 0; col < imgSource.width(); ++col )
		{
			xPos = min( (unsigned int) max( (int) col + xOffset, 0 ), imgSource.width() - 1 );

			for( unsigned int channel = 0; channel < 3; ++channel )
			{
				imgResult[ row ][ col ][ channel ] =
					imgSource[ row ][ col ][ channel ] / 2 +
					( imgSource[ yPos ][ xPos ][ channel ] ^ 0xFF ) / 2;
			}
		}
	}

	delete data;
	return true;
}
#pragma endregion

#pragma region gaussian blur
BOOL CALLBACK gaussianSetupProc(	HWND hWndDialog, UINT message,
									WPARAM wParam, LPARAM lParam )
{
	switch( message )
	{
	case WM_INITDIALOG:
		SetDlgItemText( hWndDialog, IDC_EDIT_GAUSSIAN, L"1" );
		break;
	case WM_COMMAND:
		switch( LOWORD( wParam ) )
		{
		case IDOK:
			{
				wchar_t radius[32];

				if( GetDlgItemText( hWndDialog, IDC_EDIT_GAUSSIAN, radius, 32 ) == 0 )
				{
					MessageBox( hWndDialog, L"Invalid input", L"Message", MB_OK | MB_ICONEXCLAMATION );
					return FALSE;
				}
				gaussianSettings.radius = _wtoi( radius );
			}
		case IDCANCEL:
			EndDialog( hWndDialog, wParam );
			return TRUE;
		}
		break;
	}
	return FALSE;
}

bool gaussSetup( Task *task )
{
	MainWindow *mainWindow = MainWindow::instance();
	if( DialogBox(
		mainWindow->hInstance(),
		MAKEINTRESOURCE( IDD_DIALOG_GAUSSIAN ),
		mainWindow->hWnd(),
		(DLGPROC) &gaussianSetupProc ) == IDOK )
	{
		//Allocate a structure and copy the settings. This structure is used in
		//the emboss method.
		task->_data = new GaussianSettings( gaussianSettings );
		return true;
	}
	return false; //The user cancelled the operation.
}

// gemaakt door Gerco Koks
// gebruikte masker
//	1	2	1
//	2	4	2
//	1	2	1
bool gauss( Image &imgSource, Image &imgResult, Task *task )
{
	// instellen padding grote
	int paddedSize = 1;
	// paddingklasse instantieren
	Padding pad;
	// imgSource zero padding geven
	Image paddedImage = pad.zeroPadding( imgSource, paddedSize, task );
	
	Mask m;
	// samenstellen van het filter
	m.topLeft = 1;		m.topMid = 2;		m.topRight = 1;
	m.midLeft = 2;		m.midMid = 4;		m.midRight = 2;
	m.bottomLeft = 1;	m.bottomMid = 2;	m.bottomRight =	1;
	
	// ik moet nog uitzoeken hoe de radius variabele gebruikt moet worden
	m.factor = 16;
	m.offset = 1;

	// klasse generic instantieren
	Generic gen;
	// voer de convolutie uit
	if( gen.convolute( paddedImage, imgResult, paddedSize, m ) )
		return true;
	else
		return false;
}
#pragma endregion

/*
	Naam: Mark van der Steen
*/
#pragma region sobelHorizontal
bool sobelHorizontalSetup( Task *task )
{
	return true;	
}

/*
	Naam: Mark van der Steen
*/
bool sobelHorizontal( Image &imgSource, Image &imgResult, Task *task )
{
	// padding grote bepalen
	int paddedSize = 1;
	
	Padding pad;
	Image paddedImage = pad.zeroPadding( imgSource, paddedSize, task );

	Mask m;
	// samenstellen van het filter
	m.topLeft = 1;		m.topMid = 2;		m.topRight = 1;
	m.midLeft = 0;		m.midMid = 0;		m.midRight = 0;
	m.bottomLeft = -1;	m.bottomMid = -2;	m.bottomRight =	-1;

	m.factor = 1;
	m.offset = 100;
	
	// voer de convolutie uit
	Generic gen;
	if( gen.convolute( paddedImage, imgResult, paddedSize, m ) )
		return true;
	else
		return false;
}
#pragma endregion

/*
	Naam: Mark van der Steen
*/
#pragma region sobelVertical
bool sobelVerticalSetup( Task *task )
{
	return true;
}

bool sobelVertical( Image &imgSource, Image &imgResult, Task *task )
{
	int paddedSize = 1;
	Padding pad;
	Image paddedImage = pad.zeroPadding( imgSource, paddedSize, task );

	Mask m;
	// Samenstellen van het filter voor de sobelfilter
	m.topLeft = 1;		m.topMid = 0;		m.topRight = -1;
	m.midLeft = 2;		m.midMid = 0;		m.midRight = -2;
	m.bottomLeft = 1;	m.bottomMid = 0;	m.bottomRight =	-1;

	m.factor = 1;
	m.offset = 100;
	
	// Aanroep van de standaard convolutie
	Generic gen;
	if( gen.convolute( paddedImage, imgResult, paddedSize, m ) )
		return true;
	else
		return false;
}
#pragma endregion

#pragma region laplace
bool laplaceSetup( Task *task )
{
	return true;
}

/*
	gemaakt door gerco koks
	0	-2	0
	-2	11	-2
	0	-2	0
*/
bool laplace( Image &imgSource, Image &imgResult, Task *task )
{
	// padding grote bepalen
	int paddedSize = 1;
	// padding klasse instantieren
	Padding pad;
	// imgSource zero padding geven
	Image paddedImage = pad.zeroPadding( imgSource, paddedSize, task );
	
	Mask m;
	//masker samenstellen
	m.topLeft = 0;		m.topMid = -2;		m.topRight = 0;
	m.midLeft = -2;		m.midMid = 11;		m.midRight = -2;
	m.bottomLeft = 0;	m.bottomMid = -2;	m.bottomRight = 0;
	m.factor = 3; m.offset = 0;

	// klasse generic instantieren
	Generic gen;
	// convolutie uitvoeren
	if( gen.convolute( paddedImage, imgResult, paddedSize, m ) )
		return true;
	else
		return false;
	
	return true;
}
#pragma endregion

#pragma region discreteCosineTransform
bool discreteCosineTransformSetup( Task *task )
{
	return true;
}

bool discreteCosineTransform( Image &imgSource, Image &imgResult, void *data )
{
	return true;
}
#pragma endregion
